<html>
<head>


<script>
<!--

	function _Render() {
			//#### 
		document.write("<form name='ComplexSorter' class='Cn'>" +
				"<div class='rcComplexSorter'>" +
					"<div>" +
						"<select name='rcComplexSorter_Available' multiple='true'></select>" +

						"<div class='lr'>" +
							"<img src='r.gif' title='Include column in sort.' onClick='Move(1);' />" +
							"<br/><img src='l.gif' title='Remove column from sort.' onClick='Move(0);' />" +
						"</div>" +

						"<select name='rcComplexSorter' multiple='true'></select>" +

						"<div class='ud'>" +
							"<img src='u.gif' title='Promote column.' onClick='Order(1);' />" +
							"<br/><img src='d.gif' title='Demote column.' onClick='Order(0);' />" +
							"<br/><img src='flip.gif' title='Invert column sort order.' onClick='Invert();' />" +
						"</div>" +
					"</div>" +

					"<div class='foot'>" +
						"<a href='javascript:void(null);' onClick=''>Sort</a>" +
						"<a href='javascript:void(null);' onClick=''>Cancel</a>" +
					"</div>" +
				"</div>" +
			"</form>"
		);
	}

	function Populate(sCurrentOrderBy, a_sColumnNames, a_sDisplayNames) {
		var a_sOrderBy = Normalize(sCurrentOrderBy).split(',');
		var iOrderByCount = a_sOrderBy.length;
		var iColumnCount = (a_sColumnNames ? a_sColumnNames.length : null);
		var oOption, a_sClause, sOrderByColumn, sColumnName, bIsDecending, i, j;

			//#### Clear the current .options (if any) of both g_oAvailable and g_oSort
		while (g_oAvailable.options[0]) {
			g_oAvailable.options[0] = null;
		}
		while (g_oSort.options[0]) {
			g_oSort.options[0] = null;
		}

			//#### If a_sColumnNames was valid and it's .length matches a_sDisplayNames's
		if (iColumnCount && iColumnCount == a_sDisplayNames.length) {
				//#### Traverse the a_sColumnNames, Trim'ing each entry as we go in prep for the loops below
			for (i = 0; i < iColumnCount; i++) {
				a_sColumnNames[i] = Trim(a_sColumnNames[i]).toLowerCase();
			}

				//#### Traverse the a_sOrderBy, Trim'ing each entry as we go in prep for the loops below
			for (i = 0; i < iOrderByCount; i++) {
					//#### Determine the a_sClause for the current sCurrentOrderBy entry and reset the oOption for this loop
				a_sClause = Normalize(Trim(a_sOrderBy[i])).toLowerCase().split(' ');
				oOption = null;

					//#### Determine the .length of the a_sClause and process accordingly
				switch (a_sClause.length) {
						//#### If only the column name was specified, set sOrderByColumn and bIsDecending accordingly
					case 1: {
						sOrderByColumn = a_sClause[0];
						bIsDecending = false;
						break;
					}
						//#### If the column name and a sort order was defined
					case 2: {
							//#### Set the sOrderByColumn then determine if the sort order bIsDecending
							//####     NOTE: We do not validate the sort order here, just determine if it's decending or not
						sOrderByColumn = a_sClause[0];
						bIsDecending = (a_sClause[1] == 'desc' || a_sClause[1] == 'descending')
						break;
					}
				}

					//#### If we have a real sOrderByColumn
				if (sOrderByColumn != '') {
						//#### Traverse the passed a_sColumnNames (and a_sDisplayNames by proxy)
					for (j = 0; j < iColumnCount; j++) {
							//#### If the current sOrderByColumn matches the current a_sColumnName
						if (sOrderByColumn == a_sColumnNames[j]) {
								//#### 
							if (bIsDecending) {
									//#### Setup the oOption to add into the current sort and fall from the inner loop
								g_oSort.options[g_oSort.options.length] = new Option(a_sDisplayNames[j] + ', Descending', a_sColumnNames[j] + ' DESC');
							}
								//#### 
							else {
									//#### Setup the oOption to add into the current sort and fall from the inner loop
								g_oSort.options[g_oSort.options.length] = new Option(a_sDisplayNames[j], a_sColumnNames[j]);
							}

								//#### Reset the value of the current a_sColumnName so that we don't include it in the g_oAvailable select later
							a_sColumnNames[j] = '';
							break;
						}
					}
				}
			}

				//#### Traverse the a_sColumnNames again
			for (i = 0; i < iColumnCount; i++) {
					//#### If the current a_sColumnName is valid/has not been found above, include it within the g_oAvailable .options
				if (a_sColumnNames[i] != '') {
					g_oAvailable.options[g_oAvailable.options.length] = new Option(a_sDisplayNames[i], a_sColumnNames[i]);
				}
			}
		}
	}

	function Move(bToSort) {
		var oSource, oDestination, oOption, iSelected, iDestination, bStripSort;

			//#### If we are supposed to .Move the option bTo(the)Sort, set the oSource, oDestination and bStripSort accordingly
		if (bToSort) {
			oSource = g_oAvailable;
			oDestination = g_oSort;
			bStripSort = false;
		}
			//#### Else we are supposed to .Move the option from the sort, so set the oSource, oDestination and bStripSort accordingly
		else {
			oDestination = g_oAvailable;
			oSource = g_oSort;
			bStripSort = true;
		}

			//#### Determine the iSelected and iDestination indexes
		iSelected = oSource.selectedIndex;
		iDestination = oDestination.options.length;

			//#### If the iSelected and iDestination indexes are valid
		if (iSelected >= 0 && iSelected < oSource.options.length && iDestination >= 0) {
				//#### Create the oOption based on the oSource .text and .value
			oOption = new Option(oSource.options[iSelected].text, oSource.options[iSelected].value, false, false);

				//#### If we are supposed to bStrip(the)Sort order from the new Option and a DESCending sort order is defined on the .value
			if (bStripSort && oOption.value.indexOf(' DESC') == (oOption.value.length - 5)) {
					//#### Peal off the DESCending sort order text from the .value and the .text
				oOption.value = oOption.value.substr(0, oOption.value.length - 5);
				oOption.text = oOption.text.substr(0, oOption.text.length - 12);
			}

				//#### Place the oOption into the oDestination, remove the .option from the oSource and set the .selectedIndex of the oDestination
			oDestination.options[iDestination] = oOption;
			oSource.options[iSelected] = null;
			oDestination.selectedIndex = iDestination;
		}
	}

	function Order(bUp) {
		var iSelected = g_oSort.selectedIndex;
		var iLength = g_oSort.options.length;
		var sValue, sText, iDestination;

			//#### If the iSelected index is valid
		if (iSelected >= 0 && iSelected < iLength) {
				//#### Determine the iDestination index based on the passed bUp
			iDestination = (bUp ? iSelected - 1 : iSelected + 1);

				//#### If the iDestination index is valid
			if (iDestination >= 0 && iDestination < iLength) {
					//#### Swap the .value and .text between the iSelected and iDestination indexes
				sValue = g_oSort.options[iSelected].value;
				sText = g_oSort.options[iSelected].text;
				g_oSort.options[iSelected].value = g_oSort.options[iDestination].value;
				g_oSort.options[iSelected].text = g_oSort.options[iDestination].text;
				g_oSort.options[iDestination].value = sValue;
				g_oSort.options[iDestination].text = sText;

					//#### Reset the .selectedIndex to the iDestination (so the focus follows the moved .option)
				g_oSort.selectedIndex = iDestination;
			}
		}
	}

	function Invert() {
		var iSelected = g_oSort.selectedIndex;
		var oOption, iValueLength;

			//#### If the iSelected index is valid
		if (iSelected >= 0 && iSelected < g_oSort.options.length) {
				//#### Collect the iSelected oOption and it's iValue('s)Length
			oOption = g_oSort.options[iSelected];
			iValueLength = oOption.value.length;

				//#### If the DESCending sort order is not yet defined on the .value
			if (oOption.value.indexOf(' DESC') != (iValueLength - 5)) {
					//#### Append the DESCending sort order text onto the .value and the .text
				oOption.value = oOption.value + ' DESC';
				oOption.text = oOption.text + ', Descending';
			}
				//#### Else the DESCending sort order is defined on the .value
			else {
					//#### Peal off the DESCending sort order text from the .value and the .text
				oOption.value = oOption.value.substr(0, iValueLength - 5);
				oOption.text = oOption.text.substr(0, oOption.text.length - 12);
			}

				//#### Reset the .selectedIndex
			g_oSort.selectedIndex = iSelected;
		}
	}



function Trim(sString) {
   return String(sString).replace(/^\s*|\s*$/g, '');
}

function Normalize(sString) {
	return String(sString).replace(/\s{2,}/g, ' ');
}

//-->
</script>

<style>
<!--
.UpArrow {
	font-family:Souvenir, Verdana, Arial, Helvetica, sans-serif;
	font-size: 12px;
}
.DownArrow {
	font-family:Souvenir, Verdana, Arial, Helvetica, sans-serif;
	font-size: 16px;
}

.UpArrow2 {
	font-family: sans-serif;
	font-size: 12px;
}
.DownArrow2 {
	font-family: sans-serif;
	font-size: 16px;
}

.verticaltext {
writing-mode: tb-rl;
filter: flipv fliph;
}





.rcComplexSorter {
	border: 1px solid #efefef;
	background: #ffffff;
	padding: 2px;
	display: table;
	_position: absolute;	/* IE only BS to keep div from being "width: 100%;" */
}

.rcComplexSorter a {
	border: 1px solid #efefef;
	color: #6f6f6f;
	padding: 2px;
	margin: 4px;
	text-decoration: none;
}

.rcComplexSorter select {
	border: 2px solid #efefef;
	width: 175px;
	height: 90px;
	float: left;
}

.rcComplexSorter img {
	margin: 0px 4px 0px 8px;
	cursor: pointer;
}

.rcComplexSorter div {
	text-align: left;
	float: left;
}
.rcComplexSorter div.lr {
	margin-top: 28px;
}
.rcComplexSorter div.ud {
	margin-top: 16px;
}
.rcComplexSorter div.foot {
	clear: both;
	text-align: center;
	float: none;
	line-height: 27px;
}


-->
</style>

</head>
<body>




<p class='UpArrow'>&Lambda;</p>
<p class='DownArrow'>&nu; v</p>

<p class='UpArrow2'>&Lambda;</p>
<p class='DownArrow2'>v</p>



&#8362;

<p>&and; &nbsp; &uarr; &nbsp; ^ &nbsp; ^ &nbsp; &Lambda; &nbsp; &Delta;
<p>&or; &nbsp; &darr; &nbsp; &nu; &nbsp; v &nbsp; V &nbsp; &nabla;

<p class='verticaltext'> &lt; &gt; </p>




<p>

<!--
<form name='ComplexSorter' class='Cn'>
	<div class='rcComplexSorter'>
		<div>
			<select name='rcComplexSorter_Available' multiple='true'></select>

			<div class='lr'>
				<img src='r.gif' title='Include column in sort.' onClick='Move(1);' />
				<br/><img src='l.gif' title='Remove column from sort.' onClick='Move(0);' />
			</div>

			<select name='rcComplexSorter' multiple='true'></select>

			<div class='ud'>
				<img src='u.gif' title='Promote column.' onClick='Order(1);' />
				<br/><img src='d.gif' title='Demote column.' onClick='Order(0);' />
				<br/><img src='flip.gif' title='Invert column sort order.' onClick='Invert();' />
			</div>
		</div>

		<div class='foot'>
			<a href='javascript:void(null);' onClick=''>Sort</a>
			<a href='javascript:void(null);' onClick=''>Cancel</a>
		</div>
	</div>
</form>
-->

<input type=button onclick='Test();' value='test'>

<script>
<!--
	_Render();


	var g_oAvailable = document.forms['ComplexSorter'].elements['rcComplexSorter_Available'];
	var g_oSort = document.forms['ComplexSorter'].elements['rcComplexSorter'];


	function Test() {
		Populate('  Column1 DESC,  Column5, Column2   ASC  ,  Column4 DESC ',
			['Column1','Column2  ',' Column3','Column4','Column5','Column6'],
			['Column #1','Column #2','Column #3','Column #4','Column #5','Column #6']
		);
	}
//-->
</script>


</body>
</html>